#ifdef _WIN32
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "Ws2_32.lib")
#else
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <signal.h>

#ifndef BUF_SIZE
#define BUF_SIZE 2048
#endif

// 与 udp_server.c 对应的数据结构（本地同端字节序）
#pragma pack(push, 1)
typedef struct {
    uint16_t datatype;
    uint16_t blank;
    uint16_t datetime;
    uint16_t mode;
    uint16_t en_driver_tmc1;
    uint16_t en_passenger_tmc1;
    uint16_t en_passenger_mp1;
    uint16_t en_passenger_mp2;
    uint16_t en_driver_tmc2;
    uint16_t en_passenger_tmc2;
    uint16_t en_all;
    uint16_t weight_a1;
    uint16_t weight_b1;
    uint16_t weight_b2;
    uint16_t weight_a2;
} global_train_data;

typedef struct {
    uint16_t datatype;
    uint16_t sn;
    uint16_t datetime;

    uint8_t  target_temp;
    uint8_t  driver_temp;
    uint8_t  passenger_temp;
    uint8_t  driver_return_temp;
    uint8_t  passenger_new_temp;
    uint8_t  driver_new_temp;
    uint8_t  blank1;
    uint8_t  blank2;

    uint16_t mode_data;
    uint16_t enpressor_data;
    uint16_t new_wind_data;
    uint16_t new_wind_data2;

    uint16_t blank3;
    uint16_t blank4;
    uint16_t blank5;
    uint16_t blank6;
    uint16_t blank7;
} single_train_data;

typedef struct {
    uint16_t datatype;
    uint16_t sn;
    uint16_t datetime;
    uint8_t  target_temp;
    uint8_t  blank;

    uint16_t blank3;
    uint16_t blank4;
    uint16_t blank5;
    uint16_t blank6;
    uint16_t blank7;

    uint8_t  new_wind_data;
    uint8_t  blank2;

    uint16_t blank8;
    uint16_t blank9;
    uint16_t blank10;
    uint16_t blank11;
    uint16_t blank12;
} single_train_order_data;
#pragma pack(pop)

static void hex_dump(const unsigned char *buf, size_t len) {
    for (size_t i = 0; i < len; ++i) {
        printf("%02X ", buf[i]);
        if ((i + 1) % 16 == 0) printf("\n");
    }
    if (len % 16 != 0) printf("\n");
}

static void print_global(const global_train_data *d, size_t n) {
    printf("[global_train_data] bytes=%zu\n", n);
    printf("  datatype=%u blank=%u datetime=0x%04X mode=%u\n",
           d->datatype, d->blank, d->datetime, d->mode);
    printf("  energy tmc1=%u, tmc2=%u, mp1=%u, mp2=%u, all=%u\n",
           d->en_passenger_tmc1, d->en_passenger_tmc2, d->en_passenger_mp1, d->en_passenger_mp2, d->en_all);
    printf("  weight A1=%u B1=%u B2=%u A2=%u\n",
           d->weight_a1, d->weight_b1, d->weight_b2, d->weight_a2);
}

static void print_single(const single_train_data *d, size_t n) {
    printf("[single_train_data] bytes=%zu\n", n);
    printf("  datatype=%u sn=%u datetime=0x%04X\n", d->datatype, d->sn, d->datetime);
    printf("  target=%u driver=%u passenger=%u driver_return=%u passenger_new=%u driver_new=%u\n",
           d->target_temp, d->driver_temp, d->passenger_temp, d->driver_return_temp, d->passenger_new_temp, d->driver_new_temp);
    printf("  mode=%u enpressor=%u new_wind1=%u new_wind2=%u\n",
           d->mode_data, d->enpressor_data, d->new_wind_data, d->new_wind_data2);
}

static void print_order(const single_train_order_data *d, size_t n) {
    printf("[single_train_order_data] bytes=%zu\n", n);
    printf("  datatype=%u sn=%u datetime=0x%04X target=%u new_wind=%u\n",
           d->datatype, d->sn, d->datetime, d->target_temp, d->new_wind_data);
}

static void parse_and_print(const unsigned char *buf, size_t n) {
    if (n < sizeof(uint16_t)) {
        printf("[unknown] too short: %zu bytes\n", n);
        return;
    }
    const uint16_t *ptype = (const uint16_t *)buf;
    uint16_t datatype = *ptype; // 与服务端同端主机序即可

    switch (datatype) {
        case 1:
            if (n >= sizeof(global_train_data)) {
                const global_train_data *d = (const global_train_data *)buf;
                print_global(d, n);
                return;
            }
            break;
        case 2:
            if (n >= sizeof(single_train_data)) {
                const single_train_data *d = (const single_train_data *)buf;
                print_single(d, n);
                return;
            }
            break;
        case 3:
            if (n >= sizeof(single_train_order_data)) {
                const single_train_order_data *d = (const single_train_order_data *)buf;
                print_order(d, n);
                return;
            }
            break;
        default:
            break;
    }

    printf("[unknown] datatype=%u, len=%zu (hex dump below)\n", datatype, n);
    hex_dump(buf, n);
}

static void usage(const char *prog) {
    fprintf(stderr,
            "Usage: %s [--ip <bind_ip>] [--port <port>] [--multicast <group_ip>] [--reuse] [--timeout <ms>]\n"
            "  Defaults: --ip 127.0.0.1 --port 12345\n"
            "  Examples:\n"
            "    %s\n"
            "    %s --ip 0.0.0.0 --port 9000\n"
            "    %s --port 9000 --reuse\n"
            "    %s --multicast 239.0.0.1 --port 9000\n",
            prog, prog, prog, prog, prog);
}

// 全局变量用于优雅退出
static volatile int running = 1;

#ifdef _WIN32
BOOL WINAPI console_handler(DWORD signal) {
    if (signal == CTRL_C_EVENT || signal == CTRL_CLOSE_EVENT) {
        running = 0;
    }
    return TRUE;
}
#else
void handle_signal(int sig) {
    (void)sig;
    running = 0;
}
#endif

int main(int argc, char **argv) {
    const char *bind_ip = "127.0.0.1";   // 与 udp_server.c 默认保持一致
    int port = 12345;                    // 与 udp_server.c 默认保持一致
    const char *mcast_ip = NULL;        // 可选组播地址
    int enable_reuse = 0;               // 端口复用
    int timeout_ms = 0;                 // 可选超时

    for (int i = 1; i < argc; ++i) {
        if (strcmp(argv[i], "--ip") == 0 && i + 1 < argc) {
            bind_ip = argv[++i];
        } else if (strcmp(argv[i], "--port") == 0 && i + 1 < argc) {
            port = atoi(argv[++i]);
        } else if (strcmp(argv[i], "--multicast") == 0 && i + 1 < argc) {
            mcast_ip = argv[++i];
        } else if (strcmp(argv[i], "--reuse") == 0) {
            enable_reuse = 1;
        } else if (strcmp(argv[i], "--timeout") == 0 && i + 1 < argc) {
            timeout_ms = atoi(argv[++i]);
        } else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
            usage(argv[0]);
            return 0;
        }
    }

#ifdef _WIN32
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        fprintf(stderr, "WSAStartup failed\n");
        return 1;
    }
    SetConsoleCtrlHandler(console_handler, TRUE);
#else
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = handle_signal;
    sigaction(SIGINT, &sa, NULL);
    sigaction(SIGTERM, &sa, NULL);
#endif

    int sockfd = (int)socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
#ifdef _WIN32
        fprintf(stderr, "socket failed: %d\n", WSAGetLastError());
#else
        perror("socket");
#endif
        goto cleanup;
    }

    // 端口复用，便于和其他进程共享端口（可选）
    if (enable_reuse) {
        int one = 1;
        if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char *)&one, sizeof(one)) < 0) {
#ifdef _WIN32
            fprintf(stderr, "setsockopt(SO_REUSEADDR) failed: %d\n", WSAGetLastError());
#else
            perror("setsockopt(SO_REUSEADDR)");
#endif
        }
    }

    // 接收超时（可选）
    if (timeout_ms > 0) {
#ifdef _WIN32
        DWORD to = (DWORD)timeout_ms;
        setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&to, sizeof(to));
#else
        struct timeval tv;
        tv.tv_sec = timeout_ms / 1000;
        tv.tv_usec = (timeout_ms % 1000) * 1000;
        setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
#endif
    }

    struct sockaddr_in localaddr;
    memset(&localaddr, 0, sizeof(localaddr));
    localaddr.sin_family = AF_INET;
    localaddr.sin_port = htons((uint16_t)port);
#ifdef _WIN32
    localaddr.sin_addr.s_addr = inet_addr(bind_ip);
    if (localaddr.sin_addr.s_addr == INADDR_NONE && strcmp(bind_ip, "255.255.255.255") != 0) {
        fprintf(stderr, "Invalid bind IP: %s\n", bind_ip);
        goto cleanup_sock;
    }
#else
    if (inet_pton(AF_INET, bind_ip, &localaddr.sin_addr) != 1) {
        fprintf(stderr, "Invalid bind IP: %s\n", bind_ip);
        goto cleanup_sock;
    }
#endif

    if (bind(sockfd, (struct sockaddr *)&localaddr, sizeof(localaddr)) < 0) {
#ifdef _WIN32
        fprintf(stderr, "bind failed: %d\n", WSAGetLastError());
#else
        perror("bind");
#endif
        goto cleanup_sock;
    }

    // 如果指定了组播地址，则加入组播
    struct ip_mreq mreq;
    int joined_mcast = 0;
    if (mcast_ip) {
        memset(&mreq, 0, sizeof(mreq));
#ifdef _WIN32
        mreq.imr_multiaddr.s_addr = inet_addr(mcast_ip);
        mreq.imr_interface.s_addr = inet_addr(bind_ip);
        if (mreq.imr_multiaddr.s_addr == INADDR_NONE) {
            fprintf(stderr, "Invalid multicast IP: %s\n", mcast_ip);
            goto cleanup_sock;
        }
#else
        if (inet_pton(AF_INET, mcast_ip, &mreq.imr_multiaddr) != 1 ||
            inet_pton(AF_INET, bind_ip, &mreq.imr_interface) != 1) {
            fprintf(stderr, "Invalid multicast or interface IP\n");
            goto cleanup_sock;
        }
#endif
        if (setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char *)&mreq, sizeof(mreq)) < 0) {
#ifdef _WIN32
            fprintf(stderr, "IP_ADD_MEMBERSHIP failed: %d\n", WSAGetLastError());
#else
            perror("IP_ADD_MEMBERSHIP");
#endif
            goto cleanup_sock;
        }
        joined_mcast = 1;
    }

    printf("UDP client listening on %s:%d%s\n",
           bind_ip, port, mcast_ip ? " (multicast joined)" : "");
    printf("Hint: default matches udp_server.c -> target %s:%d\n", "127.0.0.1", 12345);

    unsigned char buffer[BUF_SIZE];

    // 主循环
    while (running) {
        struct sockaddr_in peer;
        socklen_t peer_len = (socklen_t)sizeof(peer);
        memset(&peer, 0, sizeof(peer));
        int n = (int)recvfrom(sockfd, (char *)buffer, sizeof(buffer), 0,
                               (struct sockaddr *)&peer, &peer_len);
        if (n < 0) {
#ifdef _WIN32
            int err = WSAGetLastError();
            if (timeout_ms > 0 && (err == WSAETIMEDOUT)) { 
                // 超时，继续
                continue;
            }
            if (err == WSAEINTR) {
                // 被信号中断，继续
                continue;
            }
            fprintf(stderr, "recvfrom failed: %d\n", err);
#else
            if (timeout_ms > 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
                // 超时，继续
                continue;
            }
            if (errno == EINTR) {
                // 被信号中断，继续
                continue;
            }
            perror("recvfrom");
#endif
            break;
        }

        char addr_str[64];
#ifdef _WIN32
        snprintf(addr_str, sizeof(addr_str), "%s", inet_ntoa(peer.sin_addr));
#else
        inet_ntop(AF_INET, &peer.sin_addr, addr_str, sizeof(addr_str));
#endif
        printf("\n[RECV] %s:%d, %d bytes\n", addr_str, ntohs(peer.sin_port), n);
        hex_dump(buffer, (size_t)n);
        parse_and_print(buffer, (size_t)n);
        fflush(stdout);
    }

    // 离开组播组
    if (joined_mcast) {
        setsockopt(sockfd, IPPROTO_IP, IP_DROP_MEMBERSHIP, (const char *)&mreq, sizeof(mreq));
    }

cleanup_sock:
#ifdef _WIN32
    closesocket(sockfd);
#else
    close(sockfd);
#endif
cleanup:
#ifdef _WIN32·
    WSACleanup();
#endif
    return 0;
}
